package com.kirayous.api.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import com.kirayous.api.admin.mapper.UserRoleMapper;
import com.kirayous.api.admin.service.IRoleMenuService;
import com.kirayous.api.admin.service.IRoleResourceService;
import com.kirayous.api.service.impl.FilterInvocationSecurityMetadataSourceImpl;
import com.kirayous.common.PageResult;
import com.kirayous.common.constant.CommonConst;
import com.kirayous.common.dto.RoleDTO;
import com.kirayous.common.entity.Role;
import com.kirayous.api.admin.mapper.RoleMapper;
import com.kirayous.api.admin.service.IRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kirayous.common.entity.RoleMenu;
import com.kirayous.common.entity.RoleResource;
import com.kirayous.common.entity.UserRole;
import com.kirayous.common.exception.MyServeException;
import com.kirayous.common.utils.PageUtils;
import com.kirayous.common.vo.ConditionVO;
import com.kirayous.common.vo.RoleVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author KiraYous
 * @since 2021-09-10
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    UserRoleMapper userRoleMapper;
    @Autowired
    IRoleResourceService roleResourceService;

    @Autowired
    IRoleMenuService roleMenuService;

    /*
    * 这里出现了maven包的循环依赖问题了，该模块被web模块所依赖，而FilterInvocationSecurityMetadataSourceImpl又是属于web模块下的
    * 如果当前模块添加依赖web模块，那么他们互相依赖了
    * 想想如何解决
    *
    * 解决思路：（1）网上所maven不允许出现循环依赖，因为在构建的时候会先构建被依赖的包，而出现循环依赖，会让maven不知道先构建哪个
    *             ，而解决方法就是使用一种构建插件，将出现相互依赖的包打包成一个总的包，然后各个模块再依赖这个包，这个只针对构建期有用
    *             而在开发生产，仍然会可能出现包找不到等依赖冲突的问题
    *         （2）重构代码，如果出现循环依赖，说明你项目的架构设计不是的很好
    *               有问题，所以将相互依赖的那一部分放在第三个模块中（可以理解为他们的公共模块），然后他们共同依赖第三模块
    *               在这个项目中，第三模块就相当于common模块
    *         （3）由于放在common中有点奇怪，所以我选择放在api模块中
    *
    * */
    @Autowired
    private FilterInvocationSecurityMetadataSourceImpl filterInvocationSecurityMetadataSource;

    @Override
    public List<String> listRolesByUserAuthId(Integer id) {

        return roleMapper.listRolesByUserAuthId(id);
    }

    @Override
    public List<Role> listUserRoles() {
        // 查询角色列表
        List<Role> roleList = roleMapper.selectList(new LambdaQueryWrapper<Role>()
                .select(Role::getId, Role::getRoleName));
//        return BeanCopyUtil.copyList(roleList, UserRoleDTO.class);
        return roleList;
    }

    @Override
    public PageResult<RoleDTO> listRoles(ConditionVO conditionVO) {
        // 查询角色列表
        List<RoleDTO> roleDTOList = roleMapper.listRoles(PageUtils.getLimitCurrent(), PageUtils.getSize(), conditionVO);
        // 查询总量
        Integer count = this.baseMapper.selectCount(new LambdaQueryWrapper<Role>()
                .like(StringUtils.isNotBlank(conditionVO.getKeywords()), Role::getRoleName, conditionVO.getKeywords()));
        return new PageResult<>(roleDTOList, count);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveOrUpdateRole(RoleVO roleVO) {
        // 判断角色名重复
        //TODO 可能存在bug
        Role existRole = this.baseMapper.selectOne(new LambdaQueryWrapper<Role>()
                .select(Role::getId)
                .eq(Role::getRoleName, roleVO.getRoleName()));
        if (Objects.nonNull(existRole) && !existRole.getId().equals(roleVO.getId())) {
            throw new MyServeException("角色名已存在");
        }
        // 保存或更新角色信息
        Role role = Role.builder()
                .id(roleVO.getId())
                .roleName(roleVO.getRoleName())
                .roleLabel(roleVO.getRoleLabel())
                .isDisable(CommonConst.FALSE)
                .build();
        this.saveOrUpdate(role);
        // 更新角色资源关系
        if (CollectionUtils.isNotEmpty(roleVO.getResourceIdList())) {
            if (Objects.nonNull(roleVO.getId())) {
                roleResourceService.remove(new LambdaQueryWrapper<RoleResource>()
                        .eq(RoleResource::getRoleId, roleVO.getId()));
            }
            List<RoleResource> roleResourceList = roleVO.getResourceIdList().stream()
                    .map(resourceId -> RoleResource.builder()
                            .roleId(role.getId())
                            .resourceId(resourceId)
                            .build())
                    .collect(Collectors.toList());
            roleResourceService.saveBatch(roleResourceList);
            // 重新加载角色资源信息
            filterInvocationSecurityMetadataSource.clearDataSource();
        }
        // 更新角色菜单关系
        if (CollectionUtils.isNotEmpty(roleVO.getMenuIdList())) {
            if (Objects.nonNull(roleVO.getId())) {
                roleMenuService.remove(new LambdaQueryWrapper<RoleMenu>().eq(RoleMenu::getRoleId, roleVO.getId()));
            }
            List<RoleMenu> roleMenuList = roleVO.getMenuIdList().stream()
                    .map(menuId -> RoleMenu.builder()
                            .roleId(role.getId())
                            .menuId(menuId)
                            .build())
                    .collect(Collectors.toList());
            roleMenuService.saveBatch(roleMenuList);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteRoles(List<Integer> roleIdList) {
        // 判断角色下是否有用户,删除某个角色时，我们需要有关联该角色的用户解除与这个角色的绑定才行
        //TODO 可以考虑级联删除，删除该角色，然后绑定该角色的用户角色也可以被删除，但一般不推荐这么做
        // 其实也可以考虑允许删除该角色而不管他是否有用户关联，如果设计成一个用户必须拥有至少某个角色，那么我们必须准备一个无法删除的默认角色，一旦其他角色删除了，用户的角色还原为默认角色
        Integer count = userRoleMapper.selectCount(new LambdaQueryWrapper<UserRole>()
                .in(UserRole::getRoleId, roleIdList));
        if (count > 0) {
            throw new MyServeException("该角色下存在用户与角色绑定，请到用户管理中进行解绑");
        }
        this.baseMapper.deleteBatchIds(roleIdList);
    }
}
